const NodePath = require('./path/NodePath');
const { visitorKeys } = require('../types');



// 然后实现递归的遍历，遍历到不同节点时，取出不同节点要遍历的属性，然后递归遍历

module.exports = function traverse(node, visitors, parent, parentPath, key, listKey) {
    const defination = visitorKeys.get(node.type);

    // visitor 支持 enter 和 exit 阶段，也就是进入节点调用 enter 回调函数，之后遍历子节点，之后再调用 exit 回调函数。
    // 那么分别在遍历前后调用就可以，默认如果没有指定哪个阶段就在 enter 阶段调用。
    // 为什么要分 enter 和 exit 两个阶段呢？
    // 因为 enter 阶段在遍历子节点之前，那么修改之后就可以立刻遍历子节点，
    // 而 exit 是在遍历结束之后了，所以不会继续遍历子节点。
    // 如果 enter 阶段修改了 AST 但是不想遍历新生成的子节点，可以用 path.skip 跳过遍历。
    let visitorFuncs = visitors[node.type] || {};
    if(typeof visitorFuncs === 'function') {
        visitorFuncs = {
            enter: visitorFuncs
        }
    }

    // 在遍历的时候创建 path 对象，传入 visitor
    const path = new NodePath(node, parent, parentPath, key, listKey);

    visitorFuncs.enter && visitorFuncs.enter(path);

    if(node.__shouldSkip) {
        delete node.__shouldSkip;
        return;
    }

    if (defination.visitor) {
        defination.visitor.forEach(key => {
            const prop = node[key];
             // 如果该属性是数组
            if (Array.isArray(prop)) {
                prop.forEach((childNode, index) => {
                    traverse(childNode, visitors, node, path, key, index);
                })
            } else {
                traverse(prop, visitors, node, path, key);
            }
        })
    }

    visitorFuncs.exit && visitorFuncs.exit(path);
}